# -*- coding: utf-8 -*-
#--------------------------------------------------------------------------
# Copyright (c) 2009 Barry Schwartz
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
# 
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.

#--------------------------------------------------------------------------
#
# Cython interface to ImageMagick
#

cimport python_exc

class boolean_type:
    FALSE = 0
    TRUE = 1

class channel_type:
    UNDEFINED = 0
    RED = 0x0001
    GRAY = 0x0001
    CYAN = 0x0001
    GREEN = 0x0002
    MAGENTA = 0x0002
    BLUE = 0x0004
    YELLOW = 0x0004
    ALPHA = 0x0008
    OPACITY = 0x0008
    MATTE = 0x0008              # deprecated
    BLACK = 0x0020
    INDEX = 0x0020

    UNDEFINED_CHANNEL = 0
    RED_CHANNEL = 0x0001
    GRAY_CHANNEL = 0x0001
    CYAN_CHANNEL = 0x0001
    GREEN_CHANNEL = 0x0002
    MAGENTA_CHANNEL = 0x0002
    BLUE_CHANNEL = 0x0004
    YELLOW_CHANNEL = 0x0004
    ALPHA_CHANNEL = 0x0008
    OPACITY_CHANNEL = 0x0008
    MATTE_CHANNEL = 0x0008      # deprecated
    BLACK_CHANNEL = 0x0020
    INDEX_CHANNEL = 0x0020

    All_CHANNELS = 0x002F

    # special channel types
    TRUE_ALPHA_CHANNEL = 0x0040 # extract actual alpha channel from opacity
    RGB_CHANNELS = 0x0080      # set alpha from  grayscale mask in RGB
    GRAY_CHANNELS = 0x0080
    SYNC_CHANNELS = 0x0100      # channels should be modified equally
    DEFAULT_CHANNELS = 0x0127 # ((ALL_CHANNELS | SYNC_CHANNELS) & ~OPACITY_CHANNEL)

class image_type:
    UNDEFINED = 0
    BILEVEL = 1
    GRAYSCALE = 2
    GRAYSCALE_MATTE = 3
    PALETTE = 4
    PALETTE_MATTE = 5
    TRUECOLOR = 6
    TRUECOLOR_MATTE = 7
    COLOR_SEPARATION = 8
    COLOR_SEPARATION_MATTE = 9
    OPTIMIZE = 10
    PALETTE_BILEVEL_MATTE = 11

class colorspace_type:
    UNDEFINED = 0
    Undefined = 0
    RGB = 1
    GRAY = 2
    TRANSPARENT = 3
    Transparent = 3
    OHTA = 4
    Lab = 5
    XYZ = 6
    YCbCr = 7
    YCC = 8
    YIQ = 9
    YPbPr = 10
    YUV = 11
    CMYK = 12
    sRGB = 13
    HSB = 14
    HSL = 15
    HWB = 16
    Rec601Luma = 17
    Rec601YCbCr = 18
    Rec709Luma = 19
    Rec709YCbCr = 20
    Log = 21
    CMY = 22

class storage_type:
    UNDEFINED_PIXEL = 0
    CHAR_PIXEL = 1
    DOUBLE_PIXEL = 2
    FLOAT_PIXEL = 3
    INTEGER_PIXEL = 4
    LONG_PIXEL = 5
    QUANTUM_PIXEL = 6
    SHORT_PIXEL = 7

cdef class magick_wand:

    cdef MagickWand *wand

    def __cinit__(self):
        self.wand = NewMagickWand()
        if self.wand is NULL:
            python_exc.PyErr_NoMemory() # FIX: Is this the right exception?

    def __dealloc__(self):
        if self.wand is not NULL:
            DestroyMagickWand(self.wand)

    def read_image(self, file_name):
        cdef MagickBooleanType succeeded
        if not isinstance(file_name, bytes):
            file_name = file_name.encode()
        succeeded = MagickReadImage(self.wand, file_name)
        if succeeded == MagickFalse:
            raise IOError('Failed to read image: ' + file_name.decode())

    def ping_image(self, file_name):
        cdef MagickBooleanType succeeded
        if not isinstance(file_name, bytes):
            file_name = file_name.encode()
        succeeded = MagickPingImage(self.wand, file_name)
        if succeeded == MagickFalse:
            raise IOError('Failed to ping image: ' + file_name.decode())

    def get_width(self):
        return MagickGetImageWidth(self.wand)

    def get_height(self):
        return MagickGetImageHeight(self.wand)

    def get_format(self):
        return MagickGetImageFormat(self.wand).decode()

    def get_channel_depth(self, channel):
        return MagickGetImageChannelDepth(self.wand, channel)

    def get_image_type(self):
        return MagickGetImageType(self.wand)

    def set_image_type(self, image_type):
        cdef MagickBooleanType succeeded
        succeeded = MagickSetImageType(self.wand, image_type)
        if succeeded == MagickFalse:
            raise ValueError('MagickSetImageType() failed')

    def get_colorspace(self):
        return MagickGetImageColorspace(self.wand)

    def get_alpha_channel(self):
        return MagickGetImageAlphaChannel(self.wand)

    def display(self, server_name):
        cdef MagickBooleanType succeeded
        if not isinstance(server_name, bytes):
            server_name = server_name.encode()
        succeeded = MagickDisplayImage(self.wand, server_name)
        if succeeded == MagickFalse:
            raise ValueError('MagickDisplayImage() failed')

    def export_pixels(self, x_offset, y_offset, width, height, order, storage):

        assert(storage == storage_type.CHAR_PIXEL) # FIX: Implement support for other formats.
        cdef MagickBooleanType succeeded

        if not isinstance(order, bytes):
            order = order.encode()

        # pixels = bytes(len(order) * width * height) <-- Not compatible with Python 2.6.
        pixels = b''.ljust(len(order) * width * height)
        succeeded = MagickExportImagePixels(self.wand, x_offset, y_offset,
                                            width, height, order, storage, pixels)
        if succeeded == MagickFalse:
            raise ValueError('Call to MagickExportImagePixels() failed')
        return pixels

#--------------------------------------------------------------------------

# A simple delta predictor.
cpdef apply_tiff_predictor_2(unsigned int width, unsigned int height,
                             order, storage, unsigned char *pixels):

    assert(storage == storage_type.CHAR_PIXEL) # FIX: Implement support for other formats.
    cdef int stride, row, column, i

    stride = len(order)
    for row in range(0, height):
        i = (row + 1) * stride * width
        for column in range(width - 1, 0, -1):
            for j in range(0, stride):
                i -= 1
                pixels[i] = (pixels[i] - pixels[i - stride]) & 0xFF

#--------------------------------------------------------------------------
